TalentSync

AI resume builder and ATS optimizer for job seekers. Tailored resumes, outreach, and interview prep from one profile.

User management system

Identity tables for TalentSync. Prisma public and SQLModel share User, Account, and Session. FastAPI writes sessions. Next.js reads User for getSession(). NextAuth is gone.

Repository layout#

  • frontend/prisma/schema.prisma (User, Account, Session)
  • backend/app/models/tables/user.py
  • backend/app/models/tables/enums.py (UserRole)
  • backend/app/routes/auth.py
  • frontend/lib/session.ts

Building blocks#

  • User: profile, passwordHash leftover, isVerified, role, roleSelectedAt
  • Account: Google OAuth link (provider + providerAccountId unique)
  • Session: hashed refresh token, familyId, previousToken, HMAC ipHash / userAgentHash
  • EmailVerificationToken / PasswordResetToken: unused while credentials auth is off
  • VerificationToken: leftover NextAuth email-provider table. Mapped only so Prisma/SQLModel match the existing schema.

How it fits together#

User and role#

Role is UserRole on the user row (USER, RECRUITER, ADMIN). There is no Role table.

getSession() selects id, name, email, image, role, isVerified, roleSelectedAt.

Account and session#

Prisma comment on Session: owned by the backend. Do not write it from Next.js.

Refresh rotation updates the same row, keeps familyId, and stores the previous hash so replay is visible.

OAuth vs leftover password tables#

Google login writes Account.type = "oauth". Password hash and email tokens stay on the schema for CREDENTIALS_AUTH_ENABLED.

Dependencies#

Troubleshooting#

  • Session row missing but cookie present: refresh was revoked or hashed with a different secret.
  • getSession() null with a cookie: JWT sub does not match a User.id, or secret drift.
  • Duplicate Google account: unique (provider, providerAccountId) conflict; check linking rules in oauth_login.